package jmine.tec.utils;

import java.io.Serializable;

/**
 * Uma {@link Tuple} imutavel apos construcao
 * 
 * @author takeshi.fukushima
 * @param <H>
 * @param <T>
 */
public class UnmodifiableTuple<H, T> extends AbstractTuple<H, T> implements Serializable, Cloneable {

    private final H head;

    private final T tail;

    /**
     * C'tor
     * 
     * @param head o primeiro elemento
     * @param tail o segundo elemento
     */
    public UnmodifiableTuple(H head, T tail) {
        super();
        this.head = head;
        this.tail = tail;
    }

    /**
     * @return the head
     */
    public H getHead() {
        return this.head;
    }

    /**
     * @return the tail
     */
    public T getTail() {
        return this.tail;
    }

    /**
     * Constroi uma nova tupla
     * 
     * @param <H> o tipo do primeiro elemento
     * @param <T> o tipo do segundo elemento
     * @param head o primeiro elemento
     * @param tail o segundo elemento
     * @return {@link Tuple}
     */
    public static <H, T> Tuple<H, T> newTuple(H head, T tail) {
        return newUnmodifiableTuple(head, tail);
    }

    /**
     * Constroi uma nova tupla
     * 
     * @param <H> o tipo do primeiro elemento
     * @param <T> o tipo do segundo elemento
     * @param head o primeiro elemento
     * @param tail o segundo elemento
     * @return {@link Tuple}
     */
    public static <H, T> UnmodifiableTuple<H, T> newUnmodifiableTuple(H head, T tail) {
        return new UnmodifiableTuple<H, T>(head, tail);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Tuple<H, T> toUnmodifiable() {
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public Tuple<H, T> copy() {
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public Tuple<T, H> invert() {
        return newTuple(this.tail, this.head);
    }

    /**
     * Devolve uma outra instancia deste {@link UnmodifiableTuple}, trocando o {@link #getTail()}
     * 
     * @param <T2> o novo tipo do 'tail'
     * @param newTail o novo 'tail'
     * @return {@link Tuple}
     */
    public <T2> Tuple<H, T2> setTail(T2 newTail) {
        return new UnmodifiableTuple<H, T2>(this.head, newTail);
    }

    /**
     * Devolve outra instancia deste {@link UnmodifiableTuple}, trocando o {@link #getHead()}
     * 
     * @param <HeadType> o novo tipo do 'head'
     * @param newHead o novo 'head'
     * @return {@link Tuple}
     */
    public <HeadType> Tuple<HeadType, T> setHead(HeadType newHead) {
        return new UnmodifiableTuple<HeadType, T>(newHead, this.tail);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object clone() {
        return super.clone();
    }

}
